原本 Function Call 只打算寫兩篇,不過最近參考其他框架發現 Function Calling 是會被反覆調用的,測試後發現 Spring AI 也有類似的效果
Spring AI 會先去調用符合的 Function 並取得資料,之後會再拿取得的資料確認是否還有可匹配的 Function,並將目前取得的結果再次呼叫 Function
來看看怎麼活用這樣的技巧吧
在不知道 Function Calling 會重複調用時你可能會把產品改成一個 Class,並將所有產品型號放在 List 變數中,這樣做雖然也沒問題,不過長久下來會浪費許多額外花費,因為程式會將所有產品資料包含型號一起送給 AI 處理,當資料多的時候就形成額外的花費
運用重複調用的特性,我們可以增加一隻查詢產品型號的 Function,Request 的參數是產品名稱,Response 則放產品型號的 List,若 Prompt 有提到需要列出產品型號時,Spring AI 就會主動再去調用這隻查詢型號的 Function,下面直接看 Function 如何增加
public class ProductDetaislFunction implements Function<ProductDetaislFunction.Request, ProductDetaislFunction.Response>{
public record ProductDetail(String product, List<String> models) {}
@Override
public Response apply(Request request) {
//模擬資料,企業通常會透過DB或是其他API查詢內容
List<ProductDetail> productModels= List.of(
new ProductDetail("PD-1405", List.of("1405-001","1405-002","1405-003")),
new ProductDetail("PD-1234", List.of("1234-1","1234-2","1234-3","1234-4")),
new ProductDetail("PD-1235", List.of("1235-4","1235-5")),
new ProductDetail("PD-1385", List.of("1385-1","1385-2","1385-3")),
new ProductDetail("PD-1255", List.of("1255-1")),
new ProductDetail("PD-1300", List.of("1300-1","1300-1","1300-1"))
);
//模擬查詢後回傳的結果
ProductDetail models = productModels.stream().filter(pd -> pd.product.equals(request.product())).findFirst().get();
return new Response(models);
}
@JsonInclude(Include.NON_NULL)
@JsonClassDescription("產品型號列表")
public record Request(
//參數只需帶入產品
@JsonProperty(required = false, value = "product") @JsonPropertyDescription("產品") String product
) {
}
//回傳的結果最好包含產品一起回傳,若只回傳型號清單 AI 比較容易失誤
public record Response(ProductDetail models) {
}
}
//有幾隻 Function 就有幾個 Bean,這裡的描述盡量寫清楚,也可以使用中文
@Configuration
public class AiConfig {
@Bean
public FunctionCallback productSalesInfo() {
return FunctionCallbackWrapper.builder(new ProductFunction())
.withName("ProductSalesInfo")
.withDescription("Get the products sales volume at year")
.withResponseConverter((response) -> response.products().toString())
.build();
}
//新增的Bean,描述盡量清楚,關鍵字可加上中文讓 AI 更好判斷
@Bean
public FunctionCallback productDetailsInfo() {
return FunctionCallbackWrapper.builder(new ProductDetaislFunction())
.withName("ProductDetailsInfo")
.withDescription("Get the product's model(產品型號) list")
.withResponseConverter((response) -> response.models().toString())
.build();
}
}
@GetMapping("/func")
public String func(String prompt) {
return chatModel.call(
new Prompt(prompt,
OpenAiChatOptions.builder()
// Funciton可以放多筆,也能依據 API 接口放上合適的 Function
.withFunction("ProductSalesInfo")
.withFunction("ProductDetailsInfo")
.build())
).getResult().getOutput().getContent();
}
直接看測試結果,這次詢問的問題是 請給我2023年銷售量前三名的產品,並列出該產品所有型號,使用表格方式呈現
表格的部分是採用 Markdown 格式,若有前端程式搭配就能呈現表格的內容
產品型號 | 銷售量 | 所有型號 |
---|---|---|
PD-1385 | 15000 | 1385-1, 1385-2, 1385-3 |
PD-1234 | 10000 | 1234-1, 1234-2, 1234-3, 1234-4 |
PD-1235 | 1500 | 1235-4, 1235-5 |
今天學到的內容:
凱文大叔個人的心得:AI 有了 Function Calling 便能與企業資訊系統整合,以前需要人工處理的 BI 現在能透過 AI 的推理能力協助完成,目前示範的程式雖然都只是呈現在網頁上,不過 Function 其實能呼叫任何程式,也能將分析後的資料送給其他報表程式來呈現結果,甚至結合 ERP 執行下單的動作,怎麼運用就要靠大家的想像力了
今日的程式碼: https://github.com/kevintsai1202/SpringBoot-AI-Day13.git
凱文大叔使用 Java 開發程式超過 20 年,對於 Java 生態非常熟悉,曾使用反射機制開發 ETL 框架,對 Spring 背後的原理非常清楚,目前以 Spring Boot 作為後端開發框架,前端使用 React 搭配 Ant Design
下班之餘在 Amazing Talker 擔任程式語言講師,並獲得學員的一致好評
最近剛成立一個粉絲專頁-凱文大叔教你寫程式 歡迎大家多追蹤,我會不定期分享實用的知識以及程式開發技巧
想討論 Spring 的 Java 開發人員可以加入 FB 討論區 Spring Boot Developer Taiwan
我是凱文大叔,歡迎一起加入學習程式的行列